//
//  BTSendDataManager.swift
//  TimeBoat
//
//  Created by Jun on 2022/9/7.
//  Copyright © 2022 Time Boat. All rights reserved.
//

import Foundation

@objc class BTLESendDataItem: NSObject {
    @objc var cmd: [UInt8]?
    init(cmd: [UInt8]) {
        super.init()
        self.cmd = cmd
    }
}

@objc class BTLESendDataManager: NSObject {
    @objc static let shared = BTLESendDataManager()
    private let tag = LogTag.init(rawValue: "BTLESendDataManager")
    
    private let mCenter = NotificationCenter.default
    private let SYNC_OBJECT = UUID.init()
    private var mCommandList = [BTLESendDataItem]()
    private let mConditionValue: Int = 1
    private let mLock: NSConditionLock = NSConditionLock.init()
    private var mSyncTimer: Timer?
    private var mTotalStepTimer: Timer?
    private var mLocationTimer: Timer?
    private var mThread: Thread?
    private let mDispatchQueue = DispatchQueue.init(label: "bt-le-send-data-queue")
    private var mLastSyncTimestamp: TimeInterval = 0
    
    override init() {
        super.init()
        
        createThread()
        addObserverHandle()
    }
    
    deinit {
        removeObserverHandle()
    }
    
    @objc func start() {
        Log.debug(tag: tag, "start")
    }
    
    private func addObserverHandle() {
        mCenter.addObserverBTLEDidConnectedPeripheral(observer: self, selector: #selector(didConnectedPeripheralHandle), object: nil)
    }
    
    private func removeObserverHandle() {
        mCenter.removeObserverBTLEDidConnectedPeripheral(observer: self, object: nil)
    }
    
    @objc func didConnectedPeripheralHandle() {
        invalidateSyncTimer()
        createSyncTimer()
        
        // 连接成功2秒后发送同步时间命令
        mDispatchQueue.asyncAfter(deadline: .now() + 2.0) {
            self.sendSyncTimeCMD()
        }
    }
    
    @objc func sendCommand(command: BTLESendDataItem) {
        mDispatchQueue.async {
            self.pushCommand(command: command)
        }
    }
    
    private func pushCommand(command: BTLESendDataItem) {
        objc_sync_enter(SYNC_OBJECT)
        mCommandList.append(command)
        releaseLock()
        objc_sync_exit(SYNC_OBJECT)
    }
    
    private func popCommand() -> BTLESendDataItem? {
        var item: BTLESendDataItem?
        objc_sync_enter(SYNC_OBJECT)
        if mCommandList.count > 0 {
            item = mCommandList.removeFirst()
        }
        
        objc_sync_exit(SYNC_OBJECT)
        
        return item
    }
    
    private func waitLock() {
        mLock.lock(whenCondition: mConditionValue)
    }
    
    private func releaseLock() {
        mLock.unlock(withCondition: mConditionValue)
    }
    
    private func createSyncTimer() {
        if (nil == mSyncTimer) {
            Log.debug(tag: tag, "create sync time timer")
            mSyncTimer = Timer.scheduledTimer(timeInterval: 60*60, target: self, selector: #selector(sendSyncTimeCMD), userInfo: nil, repeats: true)
            RunLoop.main.add(mSyncTimer!, forMode: .common)
        }
        
        if (nil == mLocationTimer) {
            Log.debug(tag: tag, "create location timer")
            mLocationTimer = Timer.scheduledTimer(timeInterval: 30*60, target: self, selector: #selector(sendLocationCMD), userInfo: nil, repeats: true)
            RunLoop.main.add(mLocationTimer!, forMode: .common) 
        }
        
        if (nil == mTotalStepTimer) {
            Log.debug(tag: tag, "create total step timer")
            mTotalStepTimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(sendTotalStepCMD), userInfo: nil, repeats: true)
            RunLoop.main.add(mTotalStepTimer!, forMode: .common)
        }
    }
    
    private func invalidateSyncTimer() {
        mSyncTimer?.invalidate()
        mSyncTimer = nil
        
        mLocationTimer?.invalidate()
        mLocationTimer = nil
        
        mTotalStepTimer?.invalidate()
        mTotalStepTimer = nil
    }
    
    @objc func sendSyncTimeCMD() {
        let curTimestamp = Date().timeIntervalSince1970
        if curTimestamp - self.mLastSyncTimestamp > 30 {
            self.mLastSyncTimestamp = curTimestamp
            
            Log.debug(tag: tag, "send sync cmd")
            let cmd = BTLECMDType.buildSyncTimeCommand()
            
            sendCommand(command: BTLESendDataItem(cmd: cmd))
        } else {
            Log.debug(tag: tag, "send sync cmd, less than 30 second")
        }
    }
    
    @objc func sendLocationCMD() {
        Log.debug(tag: tag, "send location cmd")
    }
    
    @objc func sendTotalStepCMD() {
        Log.debug(tag: tag, "send total step cmd")
        
        let cmd = BTLECMDType.buildTotalStepCMDHeader()
        sendCommand(command: BTLESendDataItem(cmd: cmd))
    }
    
    private func createThread() {
        if (nil == mThread) {
            mThread = Thread.init(target: self, selector: #selector(threadHandle), object: nil)
            mThread!.start()
        }
    }
    
    @objc func threadHandle() {
        var item: BTLESendDataItem?
        while (true) {
            autoreleasepool {
                item = popCommand()
                if (nil != item) {
                    BTLEBaseDataManager.shared.sendCommandBuffer(buffer: item!.cmd!)
                } else {
                    waitLock()
                }
            }
        }
    }
}
